﻿using System;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Syufy.BINLookup.Web.Helpers
{
    /// <summary>
    /// 
    /// <summary>
    /// Wrapper for calling Web API services
    /// 
    /// Example Usage:
    ///     var productsClient = new RestClient<Product>("http://localhost/api/");
    ///     
    /// Example method calls:
    ///   var getResult = productsClient.GetRequest("products/1").Result;
    ///   var postResult = productsClient.PostRequest("products", new Product { Id = 3, ProductName = "Dynamite", ProductDescription = "Acme bomb" }).Result;
    ///   productsClient.PutRequest("products/3", new Product { Id = 3, ProductName = "Dynamite", ProductDescription = "Acme bomb" }).Wait();
    ///   productsClient.DeleteRequest("products/3").Wait();
    ///   
    /// </summary>
    /// <typeparam name="T">The class being manipulated by the REST api</typeparam>
    public class RestClient<T> where T : class
    {
        private readonly string _baseAddress;

        /// <summary>
        /// REST client constructor.
        /// </summary>
        /// <param name="baseAddress">Base URL</param>
        public RestClient(string baseAddress)
        {
            _baseAddress = baseAddress;
        }

        /// <summary>
        /// Used to setup the base address, that we want json, and authentication headers for the request
        /// </summary>
        /// <param name="client">The HttpClient we are configuring</param>
        /// <param name="apiUrl">The end bit of the url we use to call the Web API method</param>
        private void SetupClient(HttpClient client, string apiUrl)
        {
            client.BaseAddress = new Uri(_baseAddress);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

        /// <summary>
        /// For getting item(s) from a Web API using GET
        /// </summary>
        /// <param name="apiUrl">Added to the base address to make the full url of the 
        /// api get method, e.g. "products/1" to get a product with an id of 1</param>
        /// <returns>The item(s) requested</returns>
        public async Task<T> GetRequest(string apiUrl)
        {
            T result = null;

            using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }))
            {
                SetupClient(client, apiUrl);

                var response = await client.GetAsync(apiUrl).ConfigureAwait(false);

                //response.EnsureSuccessStatusCode();

                await response.Content.ReadAsStringAsync().ContinueWith((Task<string> content) =>
                {
                    if (content.IsFaulted)
                    {
                        throw content.Exception;
                    }

                    result = JsonConvert.DeserializeObject<T>(content.Result);
                });
            }

            return result;
        }

        /// <summary>
        /// For creating a new item over a Web API using POST
        /// </summary>
        /// <param name="apiUrl">Added to the base address to make the full url of the 
        /// api post method, e.g. "products" to add products</param>
        /// <param name="postObject">The object to be created</param>
        /// <returns>The item created</returns>
        public async Task<T> PostRequest(string apiUrl, T postObject, string contentType = "application/json")
        {
            T result = null;

            using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }))
            {
                SetupClient(client, apiUrl);

                var response = await client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter()).ConfigureAwait(false);

                //response.EnsureSuccessStatusCode();

                await response.Content.ReadAsStringAsync().ContinueWith((Task<string> content) =>
                {
                    if (content.IsFaulted)
                    {
                        throw content.Exception;
                    }

                    result = JsonConvert.DeserializeObject<T>(content.Result);
                });
            }

            return result;
        }

        /// <summary>
        /// For updating an existing item over a Web API using PUT
        /// </summary>
        /// <param name="apiUrl">Added to the base address to make the full url of the 
        /// api put method, e.g. "products/3" to update product with id of 3</param>
        /// <param name="putObject">The object to be edited</param>
        public async Task PutRequest(string apiUrl, T putObject, string contentType = "application/json")
        {
            using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }))
            {
                SetupClient(client, apiUrl);

                var response = await client.PutAsync(apiUrl, putObject, new JsonMediaTypeFormatter()).ConfigureAwait(false);

                //response.EnsureSuccessStatusCode();
            }
        }

        /// <summary>
        /// For deleting an existing item over a Web API using DELETE
        /// </summary>
        /// <param name="apiUrl">Added to the base address to make the full url of the 
        /// api delete method, e.g. "products/3" to delete product with id of 3</param>
        public async Task DeleteRequest(string apiUrl)
        {
            using (var client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true }))
            {
                SetupClient(client, apiUrl);

                var response = await client.DeleteAsync(apiUrl).ConfigureAwait(false);

                //response.EnsureSuccessStatusCode();
            }
        }
    }
}